package hehe.container.definition;

import hehe.container.build.xml.tag.Tag;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * bean 定义类描述
 *<B>说明：</B>
 *<pre>
 * 主要用于定义类注入属性
 *</pre>
 *<B>示例：</B>
 *<pre>
 *  略
 *</pre>
 *<B>日志：</B>
 *<pre>
 *  略
 *</pre>
 *<B>注意事项：</B>
 *<pre>
 *  略
 *</pre>
 */
public class BeanDefinition
{

    /**
     *　attr 标签节点名称
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    private static final String ELEMENT_ATTR = "attr";

    /**
     *　arg 标签节点名称
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    private static final String ELEMENT_ARG = "arg";

    /**
     * 容器全局唯一id
     *<B>说明：</B>
     *<pre>
     *  如未设置,则默认为对象的类名
     *</pre>
     */
    protected String id = "";

    /**
     * 对应的类名
     *<B>说明：</B>
     *<pre>
     *  app.controller.indexController
     *</pre>
     */
    protected String ref = "";

    /**
     * 对应的类名
     *<B>说明：</B>
     *<pre>
     *  app.controller.indexController
     *</pre>
     */
    protected String clazz = "";

    /**
     * bean 作用域
     *<B>说明：</B>
     *<pre>
     *   支持单例
     *</pre>
     */
    protected String scope = "";

    /**
     * 创建后,初始化函数
     *<B>说明：</B>
     *<pre>
     *   略
     *</pre>
     */
    protected String init = "";

    /**
     * bean 定义对应的反射缓存类
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected Reflection reflection = null;

    /**
     * bean 对象属性描述
     *<B>说明：</B>
     *<pre>
     * 对应格式
     * {
     *  'age'=>{'name'=>'属性名','value'=>'属性值','ref'=>'对象路径'},
     *  'username'=>{'name'=>'属性名','value'=>'属性值',type,'ref'=>'bean id','class'=>'普通类对象'}
     *  }
     *
     *</pre>
     */
    protected List<Map<String,Object>> value = new ArrayList<>();

    /**
     * bean 属性节点列表
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected Map<String,Element> attrs = new HashMap<>();

    /**
     * 构造方法参数节点列表
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected List<Element> args = new ArrayList<>();

    /**
     * 构造方法
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public BeanDefinition() {}

    /**
     * 构造方法
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public BeanDefinition(String ref)
    {
        this.id = ref;
        this.ref = ref;
    }

    /**
     * 构造方法
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public BeanDefinition(String id,String ref)
    {
        this.id = id;
        this.ref = ref;
    }

    /**
     * 构造方法
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @param attrs bean 对象属性
     */
    public BeanDefinition(Map<String,Object> attrs)
    {
        this.setAttrs(attrs);
        this.init();
    }

    /**
     * 初始化
     *<B>说明：</B>
     *<pre>
     *  解析bean属性
     *  解析bean 构造参数
     *</pre>
     */
    private void init()
    {
        this.parseAttrs();

        this.parseArgs();

        // bean xml 定义完成使命
        this.value = null;
    }

    /**
     * bean id
     *<B>说明：</B>
     *<pre>
     *  单例或新创建
     *</pre>
     */
    public String getId()
    {
        if (!this.id.equals("")) {
            return this.id;
        }

        if (!this.ref.equals("")) {
            return this.ref;
        }

        if (!this.clazz.equals("")) {
            return this.clazz;
        }

        return null;
    }

    public String getClassName()
    {
        if (!this.ref.equals("")) {
            return this.ref;
        }

        if (!this.clazz.equals("")) {
            return this.clazz;
        }

        return "";
    }

    /**
     * bean 对应类路径
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public String getRef()
    {
        return this.ref;
    }

    public void setScope(String scope)
    {
        if (!scope.equals("")) {
            this.scope = scope;
        }
    }

    public void setId(String id)
    {
        if (!id.equals("")) {
            this.id = id;
        }

    }

    public void setRef(String ref)
    {
        if (!ref.equals("")) {
            this.ref = ref;
        }
    }

    /**
     * 是否单例
     *<B>说明：</B>
     *<pre>
     * singleton 单例
     *</pre>
     */
    public boolean isSingleton()
    {
        if (this.scope.equals("") || this.scope.equals(Tag.SINGLETON_SCOPE)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取bean 定义对应的反射缓存类
     *<B>说明：</B>
     *<pre>
     * 略
     *</pre>
     */
    public Reflection getReflection()
    {
        if (this.reflection == null) {
            try {
                Class clazz = Class.forName(this.getClassName());
                this.reflection = new Reflection(clazz);
            } catch (Exception e) {
                return null;
            }
        }

        return this.reflection;
    }

    /**
     * 设置bean的属性
     *<B>说明：</B>
     *<pre>
     * 略
     *</pre>
     */
    public void setAttr(String attrName,Object attrValue)
    {
        try {
            Field field = this.getDeclaredField(this.getClass(),attrName);
            if (field != null) {
                field.setAccessible(true);
                field.set(this,attrValue);
            }
        } catch (Exception ex) {

        }
    }

    /**
     * 设置bean的属性
     *<B>说明：</B>
     *<pre>
     * 略
     *</pre>
     */
    public void setAttrs(Map<String,Object> attrs)
    {
        for (Map.Entry<String,Object> attr : attrs.entrySet()) {
            try {
                Field field = this.getDeclaredField(this.getClass(),attr.getKey());
                if (field != null) {
                    field.setAccessible(true);
                    field.set(this,attr.getValue());
                }

            } catch (Exception e) {
                // 不做任何事情
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取类属性对象
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @param className class 对象
     * @param attrName 属性名
     * @return 属性对象
     */
    protected Field getDeclaredField(Class className, String attrName)
    {

        Field field = null ;

        try {
            field = className.getDeclaredField(attrName) ;
            return field ;
        } catch (Exception e) {
            // 找不到属性
            return null;
        }
    }

    /**
     * 根据当前配置创建bean 对象
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public Object make() throws Exception
    {
        // 获取构造方法参数
        Object[] args = this.buildArgs();
        Object bean;
        if (args.length > 0) {
            bean = this.getReflection().make(args);
        } else {
            bean = this.getReflection().make();
        }

        // 设置属性
        this.reflection.setAttrs(this.buildAttrs(),bean);

        // 调用初始化方法
        if (!this.init.equals("")) {
            this.reflection.invoke(this.init,bean);
        }

        return bean;
    }

    /**
     * 根据当前配置创建bean 对象
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public Object make(Object ... args) throws Exception
    {
        return this.reflection.make(args);
    }


    /**
     * 构建bean 属性
     *<B>说明：</B>
     *<pre>
     *  为注入对象属性做准备
     *</pre>
     */
    protected Map<String,Object> buildAttrs() throws Exception
    {
        Map<String,Object> attrsValue = new HashMap<>();

        for (Map.Entry<String,Element> entry:this.attrs.entrySet()) {
            Element ele = entry.getValue();
            attrsValue.put(ele.getName(),ele.getValue());
        }

        return attrsValue;
    }

    /**
     * 构建bean 构造方法参数
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected Object[] buildArgs()
    {
        Object[] args = new Object[this.args.size()];
        for (int i = 0,j = this.args.size();i < j;i++) {
            args[i] = this.args.get(i).getValue();
        }

        return args;
    }

    /**
     * 从节点读取bean 属性
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected void parseAttrs()
    {
        List<Element> attrs = this.findElement(ELEMENT_ATTR);
        if (attrs.size() > 0) {
            for (Element element:attrs) {
                this.attrs.put(element.getName(),element);
            }
        }
    }

    /**
     * 从节点读取bean 构造参数
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected void parseArgs()
    {
        List<Element> args = this.findElement(ELEMENT_ARG);
        if (args.size() > 0) {
            for (Element element:args) {
                this.args.add(element);
            }
        }
    }

    /**
     * 设置bean 节点属性
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public void setElementAttribute(String field,String attrName,Object attrValue)
    {
        Element element = this.getElement(field);
        element.setAttribute(attrName,attrValue);
    }

    /**
     * 设置bean 节点属性
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public void setElementAttributes(String field,Map<String,Object> attrs)
    {
        Element element = this.getElement(field);
        for (Map.Entry<String,Object> attrEntry:attrs.entrySet()) {
            element.setAttribute(attrEntry.getKey(),attrEntry.getValue());
        }
    }

    /**
     * 获取属性对应的Element
     *<B>说明：</B>
     *<pre>
     *  如Element 不存在,则创建
     *</pre>
     */
    public Element getElement(String field)
    {
        Element element = this.attrs.get(field);
        if (element == null) {
            element = new Element(field);
            this.attrs.put(field,element);
        }

        return element;
    }

    /**
     * 获取构造方法参数
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected List<Element> getArgs()
    {
        return this.args;
    }

    /**
     * 查找节点
     *<B>说明：</B>
     *<pre>
     *  一般用于查找attr,arg 标签节点
     *</pre>
     */
    protected List<Element> findElement(String elementType)
    {
        List<Element> attrs = new ArrayList<>();
        for (int i = 0,j = this.value.size();i < j;i++) {
            Map<String,Object> proto = this.value.get(i);
            Element ele = Element.make(proto);
            if (ele.isType(elementType)) {
                attrs.add(ele);
            }
        }

        return attrs;
    }
}
